# This runs in a new shell
readonly ACTION=${1} MODULE=${2} DIR=${3} URL=${4} REV=${6} CLEAR_LINE=<%= clear_line %>
readonly -i PRINTLEVEL=${7}
readonly TEMP=.zdegit_${RANDOM} TARBALL_TARGET=${DIR}/${TEMP}_tarball.tar.gz INFO_TARGET=${DIR}/.zdegit

print_error() {
  print -u2 -PR ${CLEAR_LINE}"%F{red}<%= error %>%B${MODULE}:%b ${1}%f"${2:+$'\n'${(F):-  ${(f)^2}}}
}

print_done() {
  if [[ -e ${DIR}/.gitmodules ]]; then
    local -r warn=${CLEAR_LINE}"%F{yellow}<%= warn %>%B${MODULE}:%b ${(C)1}. Module contains git submodules, which are not supported by Zim's degit and were not ${1}.%f"
    if (( PRINTLEVEL > 0 )); then
      print -PR ${warn}${2:+$'\n'${(F):-  ${(f)^2}}}
    else
      print -u2 -PR ${warn}
    fi
  elif (( PRINTLEVEL > 0 )); then
    print -PR ${CLEAR_LINE}"<%= okay %>%B${MODULE}:%b ${(C)1}"${2:+$'\n'${(F):-  ${(f)^2}}}
  fi
}

download_tarball() {
  setopt LOCAL_OPTIONS EXTENDED_GLOB
  local host repo
  if [[ ${URL} =~ <%= url_regex %> ]]; then
    host=${match[3]}
    repo=${match[4]%.git}
  fi
  if [[ ${host} != github.com || -z ${repo} ]]; then
    print_error "${URL} is not a valid github.com URL. Will not try to ${ACTION}."
    return 1
  fi
  local -r headers_target=${DIR}/${TEMP}_headers
  {
    local info_header header etag
    if [[ -r ${INFO_TARGET} ]]; then
      local -r info=("${(@f)"$(<${INFO_TARGET})"}")
      if [[ ${URL} != ${info[1]} ]]; then
        print_error "URL does not match. Expected ${URL}. Will not try to ${ACTION}."
        return 1
      fi
      # Previous REV is in line 2, reserved for future use.
      info_header=${info[3]}
    fi
    local -r tarball_url=https://api.github.com/repos/${repo}/tarball/${REV}
    if (( ${+commands[curl]} )); then
      if ! ERR=$(command curl -fsSL ${info_header:+-H} ${info_header} -o ${TARBALL_TARGET} -D ${headers_target} ${tarball_url} 2>&1); then
        print_error "Error downloading ${tarball_url} with curl" ${ERR}
        return 1
      fi
    else
      # wget returns 8 when 304 Not Modified, so we cannot use wget's error codes
      command wget -q ${info_header:+--header=${info_header}} -O ${TARBALL_TARGET} -S ${tarball_url} 2>${headers_target}
    fi
    local -i http_code
    while IFS= read -r header; do
      header=${${header## ##}%%$'\r'##}
      if [[ ${header} == HTTP/* ]]; then
        http_code=${${(s: :)header}[2]}
      elif [[ ${${(L)header%%:*}%% ##} == 'etag' ]]; then
        etag=${${header#*:}## ##}
      fi
    done < ${headers_target}
    if (( http_code == 304 )); then
      # Not Modified
      command rm -f ${TARBALL_TARGET} 2>/dev/null
      return 0
    elif (( http_code != 200 )); then
      print_error "Error downloading ${tarball_url}, HTTP code ${http_code}"
      return 1
    fi
    if [[ -z ${etag} ]]; then
      print_error "Error downloading ${tarball_url}, no ETag header found in response"
      return 1
    fi
    if ! print -R ${URL}$'\n'${REV}$'\n'"If-None-Match: ${etag}" >! ${INFO_TARGET} 2>/dev/null; then
      print_error "Error creating or updating ${INFO_TARGET}"
      return 1
    fi
  } always {
    command rm -f ${headers_target} 2>/dev/null
  }
}

untar_tarball() {
  if ! ERR=$(command tar -C ${1} --strip=1 -xzf ${TARBALL_TARGET} 2>&1); then
    print_error "Error extracting ${TARBALL_TARGET}" ${ERR}
    return 1
  fi
}

create_dir() {
  if ! ERR=$(command mkdir -p ${1} 2>&1); then
    print_error "Error creating ${1}" ${ERR}
    return 1
  fi
}

() {
  case ${ACTION} in
    install)
      {
        create_dir ${DIR} && download_tarball && untar_tarball ${DIR} && print_done installed
      } always {
        # return 1 does not change ${TRY_BLOCK_ERROR}, only changes ${?}
        (( TRY_BLOCK_ERROR = ? ))
        command rm -f ${TARBALL_TARGET} 2>/dev/null
        if (( TRY_BLOCK_ERROR )); then
          command rm -rf ${DIR} 2>/dev/null
        fi
      }
      ;;
    update)
      if [[ -r ${DIR}/.zim_degit_info ]] command mv -f ${DIR}/.zim_degit_info ${INFO_TARGET}
      if [[ ! -r ${INFO_TARGET} ]]; then
        print_error "Module was not installed using Zim's degit. Will not try to update. You can disable this with the zmodule option -z|--frozen."
        return 1
      fi
      local -r dir_new=${DIR}${TEMP}
      {
        download_tarball || return 1
        if [[ ! -e ${TARBALL_TARGET} ]]; then
          if (( PRINTLEVEL > 0 )) print -PR ${CLEAR_LINE}"<%= okay %>%B${MODULE}:%b Already up to date"
          return 0
        fi
        create_dir ${dir_new} && untar_tarball ${dir_new} || return 1
        if (( ${+commands[diff]} )); then
          LOG=$(command diff -x '.zdegit*' -x '*.zwc' -x '*.zwc.old' -qr ${DIR} ${dir_new} 2>/dev/null)
          LOG=${${LOG//${dir_new}/new}//${DIR}/old}
        fi
        if ! ERR=$({ command cp -f ${INFO_TARGET} ${dir_new} && \
            command rm -rf ${DIR} && command mv -f ${dir_new} ${DIR} } 2>&1); then
          print_error "Error updating ${DIR}" ${ERR}
          return 1
        fi
        print_done updated ${LOG}
      } always {
        command rm -f ${TARBALL_TARGET} 2>/dev/null
        command rm -rf ${dir_new} 2>/dev/null
      }
      ;;
  esac
}
